/******************************************************************************************************
Copyright (C) 2018 Mestrelab Research S.L. All rights reserved.

This file is part of the MNova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*globals date2ISOString, mainWindow, print, qsTr, settings, Application, MessageBox, MnUi, NMRSpectrum*/

/*jslint plusplus: true, indent: 4, continue: true*/

function AdvisedProcessing_Viewer() {
	"use strict";

	var dialog, showDialog, data;

	//Load GUI
	dialog = Application.loadUiFile('ricares:advisedProcessing.ui');

	//Load settings
	data = settings.value(AdvisedProcessing_Viewer.applicationFor1Hkey, "");
	if (data !== "") {
		data = (data === "true");
		dialog.widgets.gb1H.checked = data;
	}

	data = settings.value(AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor1Hkey, "");
	if (data !== "") {
		data = (data === "true");
		dialog.widgets.chbx1HAutoReference.checked = data;
	}

	data = settings.value(AdvisedProcessing_Viewer.applicationFor13Ckey, "");
	if (data !== "") {
		data = (data === "true");
		dialog.widgets.gb13C.checked = data;
	}

	data = settings.value(AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor13Ckey, "");
	if (data !== "") {
		data = (data === "true");
		dialog.widgets.chbx13CAutoReference.checked = data;
	}

	data = settings.value(AdvisedProcessing_Viewer.applicationFor2Dkey, "");
	if (data !== "") {
		data = (data === "true");
		dialog.widgets.gb2D.checked = data;
	}

	data = settings.value(AdvisedProcessing_Viewer.applicationForLP2Dkey, "");
	if (data !== "") {
		data = (data === "true");
		dialog.widgets.cbLP2D.checked = data;
	}

	data = settings.value(AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor2Dkey, "");
	if (data !== "") {
		data = (data === "true");
		dialog.widgets.chbx2DAutoReference.checked = data;
	}

	//Show dialog
	showDialog = true;
	while (showDialog) {

		//Check if cancel button has been clicked by the user
		if (!dialog.exec()) {
			return;
		}
		showDialog = false;
	}

	//Save settings
	settings.setValue(AdvisedProcessing_Viewer.applicationFor1Hkey, dialog.widgets.gb1H.checked);
	settings.setValue(AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor1Hkey, dialog.widgets.chbx1HAutoReference.checked);
	settings.setValue(AdvisedProcessing_Viewer.applicationFor13Ckey, dialog.widgets.gb13C.checked);
	settings.setValue(AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor13Ckey, dialog.widgets.chbx13CAutoReference.checked);
	settings.setValue(AdvisedProcessing_Viewer.applicationFor2Dkey, dialog.widgets.gb2D.checked);
	settings.setValue(AdvisedProcessing_Viewer.applicationForLP2Dkey, dialog.widgets.cbLP2D.checked);
	settings.setValue(AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor2Dkey, dialog.widgets.chbx2DAutoReference.checked);
}

//------------------------------STATIC ATTRIBUTES-------------------------------------
AdvisedProcessing_Viewer.applicationFor1Hkey = "Advised Processing/1H Enabled";
AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor1Hkey = "Advised Processing/Automatic Referencing by Solvent for 1H Enabled";
AdvisedProcessing_Viewer.applicationFor13Ckey = "Advised Processing/13C Enabled";
AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor13Ckey = "Advised Processing/Automatic Referencing by Solvent for 13C Enabled";
AdvisedProcessing_Viewer.applicationFor2Dkey = "Advised Processing/2D Enabled";
AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor2Dkey = "Advised Processing/Automatic Referencing by Solvent for 2D Enabled";

function AdvisedProcessing_Engine() {
	"use strict";

	//Instance attribues
	this["1H"] = settings.value(AdvisedProcessing_Viewer.applicationFor1Hkey, true);
	this.automaticReferencingFor1H = settings.value(AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor1Hkey, true);
	this["13C"] = settings.value(AdvisedProcessing_Viewer.applicationFor13Ckey, true);
	this.automaticReferencingFor13C = settings.value(AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor13Ckey, true);
	this["2D"] = settings.value(AdvisedProcessing_Viewer.applicationFor2Dkey, true);
	this.LP2D = settings.value(AdvisedProcessing_Viewer.applicationForLP2Dkey, true);
	this.automaticReferencingFor2D = settings.value(AdvisedProcessing_Viewer.applicationForAutomaticReferencingFor2Dkey, true);
	this.revisionProcessOnlyFIDSpectra = 22150;
	this.processOnlyFIDSpectra = true;
	this.useAllPagesForAutomationMode = true;

	//------------------------------INSTANCE METHODS-------------------------------------
	AdvisedProcessing_Engine.prototype.getCurrentDate = function () {

		return date2ISOString(new Date());
	};

	AdvisedProcessing_Engine.prototype.getDocumentByUUID = function (aDocumentUUID) {

		var i, documents, document, result;

		result = undefined;
		documents = mainWindow.documents();
		for (i = 0; i < documents.length; i++) {
			document = documents[i];
			if (document.uuid === aDocumentUUID) {
				result = document;
				break;
			}
		}

		return result;
	};

	AdvisedProcessing_Engine.prototype.updateStatus = function (aCurrentStatus, aNewStatus) {

		var status;

		//Update the status if it is worse.
		status = aCurrentStatus;
		if (aNewStatus > aCurrentStatus) {
			status = aNewStatus;
		}

		return status;
	};

	AdvisedProcessing_Engine.prototype.getSelectedSpectra = function (aDimension, aDocument, aExperimentType, aResultObject, aGetOnlyFIDSpectra) {

		var i, j, p, page, pages, selItems, spectra, selPages, currentDocument, currentItem, currentSpectrum;

		if (aDocument !== undefined && aDocument.uuid !== "undefined" && aDocument.uuid !== "") {
			currentDocument = aDocument;
		} else {
			currentDocument = mainWindow.activeDocument;
		}

		selItems = [];
		spectra = [];
		pages = [];
		selPages = currentDocument.selectedPages();

		if (selPages && selPages.length > 0) {
			for (p = 0; p < selPages.length; ++p) {
				for (i = 0; i < selPages[p].itemCount(); i++) {
					currentItem = selPages[p].item(i);
					if (this.pageItemIsExpectedSpectrum(aDimension, currentItem, aExperimentType)) {
						currentSpectrum = new NMRSpectrum(currentItem);
						if (aGetOnlyFIDSpectra) {
							if (currentSpectrum.fidIsFID()) {
								spectra.push(currentSpectrum);
								if (aResultObject !== undefined) {
									aResultObject.log += qsTr("\n\t[INFO]: The data of the spectrum {0} comes from FID").format(currentSpectrum.title);
								}
							} else if (aResultObject !== undefined) {
								aResultObject.log += qsTr("\n\t[WARNING]: The spectrum {0} has been skipped because its data does not come from FID").format(currentSpectrum.title);
								aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
							}
						} else {
							spectra.push(currentSpectrum);
						}
					}
				}
			}
		} else {
			selItems = currentDocument.selection();
			for (i = 0; i < selItems.length; i++) {
				currentItem = selItems[i];
				if (this.pageItemIsExpectedSpectrum(aDimension, currentItem, aExperimentType)) {
					currentSpectrum = new NMRSpectrum(currentItem);
					if (aGetOnlyFIDSpectra) {
						if (currentSpectrum.fidIsFID()) {
							spectra.push(currentSpectrum);
							if (aResultObject !== undefined) {
								aResultObject.log += qsTr("\n\t[INFO]: The data of the spectrum {0} comes from FID").format(currentSpectrum.title);
							}
						} else if (aResultObject !== undefined) {
							aResultObject.log += qsTr("\n\t[WARNING]: The spectrum {0} has been skipped because its data does not come from FID").format(currentSpectrum.title);
							aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
						}
					} else {
						spectra.push(currentSpectrum);
					}
				}
			}

			//To avoid prolems with automation projects, we use this flag in order to use all spectra of the document
			//Instead of using only the spectrum stores in the current page of the document
			if (selItems.length <= 0) {
				if (this.useAllPagesForAutomationMode !== undefined && this.useAllPagesForAutomationMode) {
					pages = currentDocument.pages();
				} else {
					pages.push(currentDocument.curPage());
				}
				for (i = 0; i < pages.length; i++) {
					page = pages[i];
					for (j = 0; j < page.itemCount(); j++) {
						currentItem = page.item(j);
						if (this.pageItemIsExpectedSpectrum(aDimension, currentItem, aExperimentType)) {
							currentSpectrum = new NMRSpectrum(currentItem);
							if (aGetOnlyFIDSpectra) {
								if (currentSpectrum.fidIsFID()) {
									spectra.push(currentSpectrum);
									if (aResultObject !== undefined) {
										aResultObject.log += qsTr("\n\t[INFO]: The data of the spectrum {0} comes from FID").format(currentSpectrum.title);
									}
								} else if (aResultObject !== undefined) {
									aResultObject.log += qsTr("\n\t[WARNING]: The spectrum {0} has been skipped because its data does not come from FID").format(currentSpectrum.title);
									aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
								}
							} else {
								spectra.push(currentSpectrum);
							}
						}
					}
				}
			}
		}

		return spectra;
	};

	AdvisedProcessing_Engine.prototype.pageItemIsExpectedSpectrum = function (aDimension, aPageItem, aExperimentType) {

		var spectrum, result;

		result = false;
		if (aPageItem.name === "NMR Spectrum") {
			spectrum = new NMRSpectrum(aPageItem);
			if (aExperimentType !== undefined && aExperimentType !== "") {
				if (spectrum.isValid() && spectrum.dimCount === aDimension) {
					if (spectrum.nucleus() === aExperimentType) {
						result = true;
					}
				}
			} else {
				if (spectrum.isValid() && spectrum.dimCount === aDimension) {
					result = true;
				}
			}
		}

		return result;
	};

	AdvisedProcessing_Engine.prototype.getExperimentType = function (aExperimentType, aDocument, aResultObject, aGetOnlyFIDSpectra) {

		var spectra;

		spectra = [];

		switch (aExperimentType) {
		case "1H":
			spectra = this.getSelectedSpectra(1, aDocument, "1H", aResultObject, aGetOnlyFIDSpectra);
			break;
		case "13C":
			spectra = this.getSelectedSpectra(1, aDocument, "13C", aResultObject, aGetOnlyFIDSpectra);
			break;
		case "2D":
			spectra = this.getSelectedSpectra(2, aDocument, undefined, aResultObject, aGetOnlyFIDSpectra);
			break;
		}

		return spectra;
	};

	AdvisedProcessing_Engine.prototype.applySpecificPhaseCorrectionTo2DSpectrum = function (aSpectrum, aProcessingOption, aProcessingTemplate, aCurrentValue, aResultObject) {

		var newValue;

		if (aSpectrum.experimentType === "2D-HMBC") {
			if (aProcessingOption === "PC[1].Method") {
				newValue = "NoPC";
				if (aCurrentValue !== newValue) {
					aResultObject.log += qsTr("\n\t\t[WARNING]: The option {0} has changed its default value from {1} to {2} because the experiment type is 2D-HMBC".format(aProcessingOption, aCurrentValue, newValue));
					aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
				}
			} else if (aProcessingOption === "PC[2].Method") {
				newValue = "Magnitude";
				if (aCurrentValue !== newValue) {
					aResultObject.log += qsTr("\n\t\t[WARNING]: The option {0} has changed its default value from {1} to {2} because the experiment type is 2D-HMBC".format(aProcessingOption, aCurrentValue, newValue));
					aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
				}
			}
		} else if (aProcessingTemplate.getParameter("FT[1].Protocol") === "None") {
			if (aProcessingOption === "PC[1].Method") {
				newValue = "NoPC";
				if (aCurrentValue !== newValue) {
					aResultObject.log += qsTr("\n\t\t[WARNING]: The option {0} has changed its default value from {1} to {2} because the spectrum is 2D and FT[1].Protocol is None".format(aProcessingOption, aCurrentValue, newValue));
					aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
				}
			} else if (aProcessingOption === "PC[2].Method") {
				newValue = "Magnitude";
				if (aCurrentValue !== newValue) {
					aResultObject.log += qsTr("\n\t\t[WARNING]: The option {0} has changed its default value from {1} to {2} because the spectrum is 2D and FT[1].Protocol is None".format(aProcessingOption, aCurrentValue, newValue));
					aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
				}
			}
		}

		return newValue;
	};

	AdvisedProcessing_Engine.prototype.applySpecificApodizationTo2DSpectrum = function (aProcessingOption, aProcessingTemplate, aCurrentValue, aResultObject) {

		var newValue, isApodizationOptionRegex, isApplyApodizationOptionRegex;

		/*jslint regexp: true*/
		isApodizationOptionRegex = /Apodization\[\d\]\..+/i;
		/*jslint regexp: false*/

		//Check if the current processing option belongs to an apodization option
		if (isApodizationOptionRegex.test(aProcessingOption)) {

			//Specific apodization for non phase sensitive spectra: F2: Sine Bell 0 and F1: Sine Square 0
			if (aProcessingTemplate.getParameter("FT[1].Protocol") === "None") {

				//Detect all apodization options realated to enabling/disabling the feature 
				/*jslint regexp: true*/
				isApplyApodizationOptionRegex = /Apodization\[\d\].+Apply$/i;
				/*jslint regexp: false*/

				if (isApplyApodizationOptionRegex(aProcessingOption)) {

					//It enables the processing option if it is the general apodization applyment option
					if (aProcessingOption === "Apodization[1].Apply" || aProcessingOption === "Apodization[2].Apply") {
						newValue = true;

					//It enables sine square apodization in F1
					} else if (aProcessingOption === "Apodization[1].Sine2.Apply") {
						newValue = true;

					//It enables sine bell apodization in F2
					} else if (aProcessingOption === "Apodization[2].Sine.Apply") {
						newValue = true;

					//By default all the options set in the configuration section (static attribute) will be disabled
					} else {
						newValue = false;
					}

				} else {

					//Set F1: Sine Square 0
					if (aProcessingOption === "Apodization[1].Sine2.Value") {
						newValue = 0;

					//Set F2: Sine Bell 0
					} else if (aProcessingOption === "Apodization[2].Sine.Value") {
						newValue = 0;
					}
				}

				//Update the information to the log
				if (newValue !== undefined && aCurrentValue !== newValue) {
					aResultObject.log += qsTr("\n\t\t[INFO]: The option {0} has changed its default value from {1} to {2} because it is a non sensitive experiment".format(aProcessingOption, aCurrentValue, newValue));
					aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED);
				}
			}
		}

		return newValue;
	};

	AdvisedProcessing_Engine.prototype.applySpecificZeroFillingTo2DSpectrum = function (aSpectrum, aProcessingOption, aCurrentValue, aThreshold, aResultObject) {

		var newValue, auxNewValue, currentValue, threshold;

		if (aProcessingOption === "ZF[1].Size") {

			currentValue = Number(aSpectrum.fidCount(1));
			if (currentValue !== undefined && !isNaN(currentValue)) {
				threshold = Number(aThreshold);
				if (threshold !== undefined && !isNaN(threshold)) {
					auxNewValue = Math.max(currentValue, threshold);
					if (auxNewValue !== undefined && !isNaN(auxNewValue)) {
						if (auxNewValue > aCurrentValue) {
							newValue = auxNewValue;
							if (aCurrentValue !== newValue) {
								aResultObject.log += qsTr("\n\t\t[INFO]: The option {0} has changed its default value from {1} to {2}".format(aProcessingOption, currentValue, newValue));
								aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED);
							}
						}
					}
				}
			}

		} else if (aProcessingOption === "ZF[2].Size") {

			currentValue = Number(aSpectrum.fidCount(2));
			if (currentValue !== undefined && !isNaN(currentValue)) {
				threshold = Number(aThreshold);
				if (threshold !== undefined && !isNaN(threshold)) {
					auxNewValue = Math.max(currentValue, threshold);
					if (auxNewValue !== undefined && !isNaN(auxNewValue)) {
						if (auxNewValue > aCurrentValue) {
							newValue = auxNewValue;
							if (aCurrentValue !== newValue) {
								aResultObject.log += qsTr("\n\t\t[INFO]: The option {0} has changed its default value from {1} to {2}".format(aProcessingOption, currentValue, newValue));
								aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED);
							}
						}
					}
				}
			}
		}

		return newValue;
	};

	AdvisedProcessing_Engine.prototype.applySpecificLinearPredictionTo2DSpectrum = function (aSpectrum, aProcessingOption, aProcessingTemplate, aCurrentValue, aThreshold, aResultObject) {

		var newValue, coefficients, threshold, numberOfPointsInFIDAux, numberOfPointsInFIDFofF1, thresholdForF1, factor;

		//Another important point is to make sure that the maximum number of coefficients does not exceed 12
		//Otherwise some instabilities could occur. 
		if (aProcessingOption === "LP[1].Forward.Coefficients") {
			coefficients = Number(aCurrentValue);
			threshold = Number(aThreshold);
			if (coefficients > threshold) {
				newValue = threshold;
				if (aCurrentValue !== newValue) {
					aResultObject.log += qsTr("\n\t\t[INFO]: The option {0} has changed its default value from {1} to {2} because is a 2D and the coefficients are greater than {3}".format(aProcessingOption, aCurrentValue, newValue, threshold));
					aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED);
				}
			}

		//If the original acquired size of the FID (F1) is <=512 data points, the maximum LP last value could be 2048, the rest should be zero filled from that point.
		//Otherwise, the last point of the Linear Processing will be the default value, currently 1024
		} else if (aProcessingOption === "LP[1].Forward.Last") {
			factor = aProcessingTemplate.getParameter("FT[1].Protocol") === "None" ? 3.0 : 1.5;
			thresholdForF1 = 512;
			numberOfPointsInFIDAux = aSpectrum.fidCount(1);
			numberOfPointsInFIDFofF1 = Number(numberOfPointsInFIDAux);
			if (numberOfPointsInFIDFofF1 !== undefined && !isNaN(numberOfPointsInFIDFofF1)) {
				if (numberOfPointsInFIDFofF1 <= thresholdForF1) {
					newValue = numberOfPointsInFIDFofF1 * factor;
					if (aCurrentValue !== newValue) {
						aResultObject.log += qsTr("\n\t\t[INFO]: The option {0} has changed its default value from {1} to {2} because is a 2D and the number of points of the FID in F1 is lower than {3}".format(aProcessingOption, aCurrentValue, newValue, thresholdForF1));
						aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED);
					}
				} else {
					newValue = aThreshold;
					if (aCurrentValue !== newValue) {
						aResultObject.log += qsTr("\n\t\t[INFO]: The option {0} has changed its default value from {1} to {2} because is a 2D and the number of points of the FID in F1 is greater than {3}".format(aProcessingOption, aCurrentValue, newValue, thresholdForF1));
						aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED);
					}
				}
			} else {
				aResultObject.log += qsTr("\n\t\t[WARNING]: Unable to get the original acquired size of the FID in F1 dimension");
				aResultObject.status = this.updateStatus(aResultObject.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
			}
		}

		return newValue;
	};

	AdvisedProcessing_Engine.prototype.applyProcessingOptions = function (aExperimentType, aSpectra, aDocument) {

		var i, spectrum, processingTemplate, result, processingOption, points, processingObject,
			currentValue, newValue, flagPropertyChanged, newPhaseCorrectionValue, newZeroFillingValue,
			newLinearPredictionValue, newApodizationValue, flagAvoidMessage, isLPOptionRegex;

		//Initialize vars
		result = {};
		result.status = 0;
		result.log = "";
		flagPropertyChanged = false;

		//Process each spectra
		for (i = 0; i < aSpectra.length; i++) {
			spectrum = aSpectra[i];
			if (spectrum.isValid()) {
				processingTemplate = spectrum.proc;
				processingObject = AdvisedProcessing_Engine.experiment[aExperimentType];

				//Advised options management
				for (processingOption in processingObject) {
					if (processingObject.hasOwnProperty(processingOption)) {
						if (!flagPropertyChanged) {
							result.log += qsTr("\n\t[INFO]: The following options have been checked in spectrum: {0}").format(spectrum.title);
							flagPropertyChanged = true;
						}

						//Get the new value and the current Value
						currentValue = processingTemplate.getParameter(processingOption);
						newValue =  processingObject[processingOption];

						//ZF only must applied if the number of points in the spectrum is less than 64 Kb
						if (processingOption === "ZF.size") {
							if (aExperimentType === "1H" || aExperimentType === "13C") {
								points = spectrum.count(1);
								if (points > 65536) {
									result.log += qsTr("\n\t\t[INFO]: The spectrum has {0} and the Zero Filling property is not needed".format(points));
									continue;
								}
							}
						}

						//Reset flag
						flagAvoidMessage = false;

						//2D spectra management
						if (spectrum.dimCount === 2) {

							//Phase Correction Management
							//The phase correction in 2D will not be performed if the spectrum is in magnitude
							//If the spectrum is both 2D and HMBC, the phase correction is not applied in F1 and magnitud is applied in F2
							//If the sepctrum is both 2D but it is not HMBC and the protocol is None the phase correction is not applied in F1 y magnitud is applied in F2
							newPhaseCorrectionValue = this.applySpecificPhaseCorrectionTo2DSpectrum(spectrum, processingOption, processingTemplate, currentValue, result);
							if (newPhaseCorrectionValue !== undefined && newPhaseCorrectionValue !== "") {
								newValue = newPhaseCorrectionValue;
							}

							//Apodization management
							newApodizationValue = this.applySpecificApodizationTo2DSpectrum(processingOption, processingTemplate, currentValue, result);
							if (newApodizationValue !== undefined && newApodizationValue !== "") {
								newValue = newApodizationValue;
							}

							//Zero Filling management
							newZeroFillingValue = this.applySpecificZeroFillingTo2DSpectrum(spectrum, processingOption, currentValue, newValue, result);
							if (newZeroFillingValue !== undefined && newZeroFillingValue !== "") {
								newValue = newZeroFillingValue;
							}

							//Linear Prediction management
							if (this.LP2D !== undefined && this.LP2D) {
								newLinearPredictionValue = this.applySpecificLinearPredictionTo2DSpectrum(spectrum, processingOption, processingTemplate, currentValue, newValue, result);
								if (newLinearPredictionValue !== undefined && newLinearPredictionValue !== "") {
									newValue = newLinearPredictionValue;
								}
							} else {

								//Check if the current processing option belongs to an LP option
								//We do not modify values if the LP option is not checked
								/*jslint regexp: true*/
								isLPOptionRegex = /LP\[\d\]\..+/i;
								/*jslint regexp: false*/
								if (isLPOptionRegex.test(processingOption)) {
									if (processingOption === "LP[1].Forward.Apply") {
										if (newValue === true) {
											newValue = false;
										}
									} else {
										newValue = currentValue;
										flagAvoidMessage = true;
									}
								}
							}

							//Automatic Referencing by Solvent management
							if (processingOption === "Ref[1].ReferenceBySolvent" || processingOption === "Ref[2].ReferenceBySolvent") {
								if (this["2D"] !== undefined && this["2D"]) {
									if (this.automaticReferencingFor2D !== undefined && !this.automaticReferencingFor2D) {
										newValue = currentValue;
										flagAvoidMessage = true;
									} else if (!currentValue) {
										newValue = true;
									}
								} else {
									newValue = currentValue;
								}
							} else if (processingOption === "AbsoluteReference[1].Apply" || processingOption === "AbsoluteReference[2].Apply") {
								if (this["2D"] !== undefined && this["2D"]) {
									if (this.automaticReferencingFor2D !== undefined && !this.automaticReferencingFor2D) {
										newValue = currentValue;
										flagAvoidMessage = true;
									}
								}
							}

						//1D spectra management
						} else {

							//Automatic Referencing by Solvent management
							if (processingOption === "Ref.ReferenceBySolvent") {
								if (this["1H"] !== undefined && this["1H"] && aExperimentType === "1H") {
									if (this.automaticReferencingFor1H !== undefined && !this.automaticReferencingFor1H) {
										newValue = currentValue;
										flagAvoidMessage = true;
									} else if (!currentValue) {
										newValue = true;
									}
								} else if (this["13C"] !== undefined && this["13C"] && aExperimentType === "13C") {
									if (this.automaticReferencingFor13C !== undefined && !this.automaticReferencingFor13C) {
										newValue = currentValue;
										flagAvoidMessage = true;
									} else if (!currentValue) {
										newValue = true;
									}
								}
							} else if (processingOption === "AbsoluteReference[1].Apply") {
								if (this["1H"] !== undefined && this["1H"] && aExperimentType === "1H") {
									if (this.automaticReferencingFor1H !== undefined && !this.automaticReferencingFor1H) {
										newValue = currentValue;
										flagAvoidMessage = true;
									}
								}
								if (this["13C"] !== undefined && this["13C"] && aExperimentType === "13C") {
									if (this.automaticReferencingFor13C !== undefined && !this.automaticReferencingFor13C) {
										newValue = currentValue;
										flagAvoidMessage = true;
									}
								}
							}
						}

						//Change the processing option if is needed
						if (currentValue !== newValue) {
							processingTemplate.setParameter(processingOption, newValue);
							if ((newPhaseCorrectionValue === undefined) && (newApodizationValue === undefined) && (newZeroFillingValue === undefined) && (newLinearPredictionValue === undefined)) {
								result.log += qsTr("\n\t\t[INFO]: The option {0} has been changed from {1} to {2}".format(processingOption, currentValue, newValue));
							}
						} else if (!flagAvoidMessage) {
							result.log += qsTr("\n\t\t[INFO]: The option {0} already had the expected value: {1}").format(processingOption, currentValue);
						}

						//Reset values
						newPhaseCorrectionValue = undefined;
						newApodizationValue = undefined;
						newZeroFillingValue = undefined;
						newLinearPredictionValue = undefined;
					}
				}

				//Perform the changes
				if (flagPropertyChanged) {
					spectrum.process(processingTemplate);
					spectrum.update();
					aDocument.update();
					spectrum.fitToHeight();
					spectrum.update();
					aDocument.update();
				}

				//Reset flag for the next spectrum
				flagPropertyChanged = false;
			} else {
				result.log += qsTr("[ERROR]: The following spectrum is not valid: {0}").format(spectrum.title);
				result.status = AdvisedProcessing_Engine.status.PERFORMED_WITH_ERRORS;
			}
		}

		return result;
	};

	AdvisedProcessing_Engine.prototype.process = function (aDocument) {

		var result, document, spectra, auxResult, experimentType, totalExperimentDisabled, flagProcessOnlyFIDSpectra;

		//Initialize vars
		result = {};
		result.status = AdvisedProcessing_Engine.status.PERFORMED;
		result.log = qsTr("[INFO]: Advised Processing processing started on {0}").format(this.getCurrentDate());
		result.logDetail = "";
		totalExperimentDisabled = 0;
		flagProcessOnlyFIDSpectra = false;

		//Get the document to be work on.
		if (aDocument !== undefined) {
			document = this.getDocumentByUUID(aDocument);
		} else {
			document = mainWindow.activeDocument;
		}

		//Check version for processin only FID spectra
		if (this.processOnlyFIDSpectra !== undefined && this.processOnlyFIDSpectra) {
			if (this.revisionProcessOnlyFIDSpectra <= Application.version.revision) {
				result.log += qsTr("\n[INFO]: Process only FID spectra feature enabled");
				flagProcessOnlyFIDSpectra = true;
			} else {
				result.log += qsTr("\n[WARNING]: Process only FID spectra feature not supported in this Mnova revision: {0}. Revision {1} or above required ").format(Application.version.revision, this.revisionProcessOnlyFIDSpectra);
				result.status = this.updateStatus(result.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
			}
		} else {
			result.log += qsTr("\n[INFO]: Process only FID spectra feature disabled");
		}

		//The process begins if the document is valid
		if (document === undefined || document.uuid === undefined) {
			result.logDetail += qsTr("\n[ERROR]: Incorrect document");
			result.status = this.updateStatus(result.status, AdvisedProcessing_Engine.status.NOT_PERFORMED);
		} else {

			//Process each type of experiment
			for (experimentType in AdvisedProcessing_Engine.experiment) {
				if (AdvisedProcessing_Engine.experiment.hasOwnProperty(experimentType)) {

					//Check if is enabled in the settings
					if (this[experimentType]) {
						spectra = this.getExperimentType(experimentType, document, result, flagProcessOnlyFIDSpectra);
						result.logDetail += qsTr("\n[INFO]: Result Detail for {0} spectra:".format(experimentType));
						if (spectra.length === 0) {
							result.logDetail += qsTr("\n\t[WARNING]: No spectra {0} found in the document".format(experimentType));
							result.status = this.updateStatus(result.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
						} else {
							if (spectra.length === 1) {
								result.logDetail += qsTr("\n\t[INFO]: It has been found a single spectrum");
							} else {
								result.logDetail += qsTr("\n\t[INFO]: It has been found {0} spectra".format(spectra.length));
							}
							//Processing options management
							auxResult = this.applyProcessingOptions(experimentType, spectra, document);
							result.logDetail += auxResult.log;
							result.status = this.updateStatus(result.status, auxResult.status);
						}
					} else {
						totalExperimentDisabled++;
						result.logDetail += qsTr("\n\t[WARNING]: The experiment type {0} is disabled".format(experimentType));
						result.status = this.updateStatus(result.status, AdvisedProcessing_Engine.status.PERFORMED_WITH_WARNINGS);
					}
				}
			}

			//None of the tests has been carried out
			if (totalExperimentDisabled === Object.keys(AdvisedProcessing_Engine.experiment).length) {
				result.status = this.updateStatus(result.status, AdvisedProcessing_Engine.status.NOT_PERFORMED);
			}
		}

		return result;
	};
}

//------------------------------STATIC ATTRIBUTES-------------------------------------
AdvisedProcessing_Engine.status = {
	PERFORMED: 0,
	PERFORMED_WITH_WARNINGS: 1,
	PERFORMED_WITH_ERRORS: 2,
	NOT_PERFORMED: 3
};

AdvisedProcessing_Engine.experiment = {};
AdvisedProcessing_Engine.experiment["1H"] = {
	"ZF.size": 65536,
	"Apodization.Apply": true,
	"Apodization.ConDif.Apply": false,
	"Apodization.Exp.Apply": false,
	"Apodization.FP.Apply": false,
	"Apodization.Gauss.Apply": false,
	"Apodization.Han.Apply": false,
	"Apodization.Lg.Apply": false,
	"Apodization.Lr.Apply": false,
	"Apodization.Parabolic.Apply": false,
	"Apodization.Sinc.Apply": false,
	"Apodization.Sine.Apply": false,
	"Apodization.Sine2.Apply": false,
	"Apodization.SineII.Apply": false,
	"Apodization.SineII2.Apply": false,
	"Apodization.Stan.Apply": true,
	"Apodization.Traf.Apply": false,
	"Apodization.Trap.Apply": false,
	"PC.Method": "Regions Analysis",
	"Ref.ReferenceBySolvent": true,
	"AbsoluteReference[1].Apply": false,
	"BC.Apply": true,
	"BC.algorithm": "Bernstein",
	"BC.PolyOrder": 3
};

AdvisedProcessing_Engine.experiment["13C"] = {
	"ZF.size": 65536,
	"Apodization.Apply": true,
	"Apodization.ConDif.Apply": false,
	"Apodization.Exp.Apply": true,
	"Apodization.Exp.Value": 2,
	"Apodization.FP.Apply": false,
	"Apodization.Gauss.Apply": false,
	"Apodization.Han.Apply": false,
	"Apodization.Lg.Apply": false,
	"Apodization.Lr.Apply": false,
	"Apodization.Parabolic.Apply": false,
	"Apodization.Sinc.Apply": false,
	"Apodization.Sine.Apply": false,
	"Apodization.Sine2.Apply": false,
	"Apodization.SineII.Apply": false,
	"Apodization.SineII2.Apply": false,
	"Apodization.Stan.Apply": true,
	"Apodization.Traf.Apply": false,
	"Apodization.Trap.Apply": false,
	"PC.Method": "Regions Analysis",
	"LP.Backward.Apply": true,
	"LP.Backward.method": "Toeplitz",
	"Ref.ReferenceBySolvent": true,
	"AbsoluteReference[1].Apply": false
};

AdvisedProcessing_Engine.experiment["2D"] = {
	"PC[1].Method": "Regions2D",
	"PC[2].Method": "Regions2D",
	"ZF[1].Size": 2048,	//Max value (act as a threshold)
	"ZF[2].Size": 2048,	//Max value (act as a threshold)
	"Apodization[1].Apply": true,
	"Apodization[1].ConDif.Apply": false,
	"Apodization[1].Exp.Apply": false,
	"Apodization[1].Gauss.Apply": false,
	"Apodization[1].Han.Apply": false,
	"Apodization[1].Lg.Apply": false,
	"Apodization[1].Lr.Apply": false,
	"Apodization[1].Parabolic.Apply": false,
	"Apodization[1].Sinc.Apply": false,
	"Apodization[1].Sine.Apply": false,
	"Apodization[1].Sine2.Apply": true,
	"Apodization[1].Sine2.Value": 90,
	"Apodization[1].SineII.Apply": false,
	"Apodization[1].SineII2.Apply": false,
	"Apodization[1].Stan.Apply": false,
	"Apodization[1].Traf.Apply": false,
	"Apodization[1].Trap.Apply": false,
	"Apodization[2].Apply": true,
	"Apodization[2].ConDif.Apply": false,
	"Apodization[2].Exp.Apply": true,
	"Apodization[2].Exp.Value": 2,
	"Apodization[2].Gauss.Apply": false,
	"Apodization[2].Han.Apply": false,
	"Apodization[2].Lg.Apply": false,
	"Apodization[2].Lr.Apply": false,
	"Apodization[2].Parabolic.Apply": false,
	"Apodization[2].Sinc.Apply": false,
	"Apodization[2].Sine.Apply": false,
	"Apodization[2].Sine.Value": -1,
	"Apodization[2].Sine2.Apply": false,
	"Apodization[2].Sine2.Value": 90,
	"Apodization[2].SineII.Apply": false,
	"Apodization[2].SineII2.Apply": false,
	"Apodization[2].Stan.Apply": true,
	"Apodization[2].Traf.Apply": false,
	"Apodization[2].Trap.Apply": false,
	"LP[1].Forward.Apply": true,
	"LP[1].Forward.method": "ZhuBax",
	"LP[1].Forward.Coefficients": 12,
	"LP[1].Forward.Last": 1024,
	"Ref[1].ReferenceBySolvent": true,
	"Ref[2].ReferenceBySolvent": true,
	"AbsoluteReference[1].Apply": false,
	"AbsoluteReference[2].Apply": false
};

function advisedProcessing() {
	'use strict';

	var advisedProcessing_Viewer;

	advisedProcessing_Viewer = new AdvisedProcessing_Viewer();
}

if (mainWindow.hasGui) {

	if (this.MnUi && this.MnUi.scripts_nmr) {

		MnUi.scripts_nmr.scripts_nmr_advisedProcessingOptions = function () {
			"use strict";

			try {
				advisedProcessing();
			} catch (ERR) {
				MessageBox.critical(ERR);
			}
		};

		MnUi.scripts_nmr.scripts_nmr_advisedProcessingLauncher = function () {
			"use strict";

			var engine;

			try {
				engine = new AdvisedProcessing_Engine();
				engine.processOnlyFIDSpectra = false;
				engine.useAllPagesForAutomationMode = false;
				engine.process();
			} catch (ERR) {
				MessageBox.critical(ERR);
			}
		};
	}
}

function advisedProcessingTest() {
	"use strict";

	var engine;

	try {
		engine = new AdvisedProcessing_Engine();
		engine.processOnlyFIDSpectra = false;
		print(engine.process());
	} catch (ERR) {
		print(ERR);
	}
}